/*
 * Amkai Enterprise Solution (AES.) 
 * Copyright 2004-2008 by Amkai(TM), Inc.
 * www.amkai.com
 * 
 * Project: webservice
 * Package: com.amkai.aes.webservice.binary.objecthandler.billing
 * File: BatchHandler.java
 * Created: 2008.11.04. 17:41:04
 * Author: polaj 
 * 
 */
package com.amkai.aes.webservice.binary.objecthandler.billing;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.amkai.aes.aescommon.billing.ThinBatch;
import com.amkai.aes.aescommon.ledger.utils.BatchUtils;
import com.amkai.aes.aescommon.passport.PassportManager;
import com.amkai.aes.aescommon.service.binary.job.billing.BatchJob;
import com.amkai.aes.aescommon.service.result.billing.BatchResult;
import com.amkai.aes.aescommon.utilities.BeanFormatter;
import com.amkai.aes.generatedbeans.billinginterval.Batch;
import com.amkai.aes.generatedbeans.billinginterval.Expected;
import com.amkai.aes.generatedbeans.billinginterval.LastUsedBatch;
import com.amkai.aes.generatedbeans.organization.Department;
import com.amkai.aes.generatedbeans.passport.Passport;
import com.amkai.aes.generatedbeans.remittanceposting.RemittancePostingSession;
import com.amkai.aes.generatedbeans.security.RoleKind;
import com.amkai.aes.server.billing.BatchManager;
import com.amkai.aes.server.session.ServiceSessionManager;
import com.amkai.aes.webservice.binary.JobObjectHandler;
import com.amkai.common.extensions.hibernate.config.SiteManager;
import com.amkai.common.extensions.hibernate.config.manager.SessionManager;

public class BatchHandler implements JobObjectHandler<BatchJob, Department, BatchResult>{

	private static final Log log = LogFactory.getLog(BatchHandler.class);
	private static final String TITLE = "Batch Handler";
	//protected Department moduleDepartment = null;
	
	@Override
	public BatchResult process(BatchJob job, Department dep) throws Exception {
		//this.moduleDepartment = dep;
		switch (job.getAction()) {
		case GET_BATCHES_BY_PERIOD:	return getBatchesByPeriod(job, dep);
		case GET_BATCHES_BY_PERIOD_FOR_ME:	return getBatchesByPeriodForMe(job, dep);
		case SAVE_BATCH:	return saveBatch(job.getBatch(),dep);
		case CLOSE_BATCH:	return closeBatch(job.getBatch(),dep);
		case RELOAD_BATCH:	return reloadBatch(job.getBatch(),dep);
		case GET_REMITTANCE_POSTING_SESSION_BY_BATCH:	return getRemittancePostingSessionsByBatch(job.getBatch());
		case GET_BATCH_BY_ID:	return getBatchByID(job, dep);
		case SET_LAST_USED_BATCH:	return setLastUsedBatch(job, dep);
		case GET_RECENT_BATCH:	return getRecentBatch(job, dep);
		}
		return null;
	}

	private List<ThinBatch> getBatchesByPeriod(SessionManager sm, long periodID) throws Exception {
		List<Batch> l = sm.execute("FROM Batch b " +
			"WHERE b.period.id=:period_id")
			.setParameter("period_id", periodID).list();
		List<ThinBatch> thinBatches = new ArrayList<ThinBatch>();
		for (Batch batch : l) {
			thinBatches.add(new ThinBatch(batch));
		}
		return thinBatches;
	}
	
	protected BatchResult getBatchesByPeriod(BatchJob job, Department dep) throws Exception {
		BatchResult result = new BatchResult();
		SessionManager sm = SiteManager.Start();
		try {
			result.setBatches(getBatchesByPeriod(sm, job.getPeriodID()));
			log.debug(String.format("Found %d batch(es)", result.getBatches().size()));
		}
		catch (Exception ex) {
			result.setException(ex);
			log.error(TITLE, ex);
		}
		finally {
			sm.closeSession();
		}
		return result;
	}
	
	protected BatchResult getBatchesByPeriodForMe(BatchJob job,Department dep) throws Exception {
		// AES-11936
		// SYSADM users got all the batches not only the own or shared batches
		BatchResult result = new BatchResult();
		SessionManager sm = SiteManager.Start();
		try {
			Passport passport = sm.get(Passport.class, ServiceSessionManager.getConnection().getRuntimePassport().getId());
			if (PassportManager.isPassportInRole(passport, RoleKind.SYSADM, dep)) {
				result.setBatches(getBatchesByPeriod(sm, job.getPeriodID()));
			}
			else {
				List<Batch> l = sm.execute("FROM Batch b " +
						"WHERE b.period.id=:period_id " +
						"AND (b.isShared=1 OR b.creator=:creator)")
						.setParameter("period_id", job.getPeriodID())
						.setParameter("creator", passport).list();
				List<ThinBatch> thinBatches = new ArrayList<ThinBatch>();
				for (Batch batch : l) {
					thinBatches.add(new ThinBatch(batch));
				}
				result.setBatches(thinBatches);
			}
			log.debug(String.format("Found %d batch(es)", result.getBatches().size()));
		}
		catch (Exception ex) {
			result.setException(ex);
			log.error(TITLE, ex);
		}
		finally {
			sm.closeSession();
		}
		return result;
	}
	
	protected BatchResult saveBatch(Batch batch, Department dep) throws Exception {
		BatchResult result = new BatchResult();
		SessionManager sm = SiteManager.Start();
		try {
			sm.openTransaction();
			BatchManager bm = new BatchManager(sm, dep);
			bm.saveBatch(batch);
			sm.commitTransaction();
		}
		catch (Exception ex) {
			sm.rollbackTransaction();
			result.setException(ex);
			log.error(TITLE, ex);
		}
		finally {
			sm.closeSession();
		}
		return result;
	}
	
	protected BatchResult closeBatch(Batch batch, Department dep) throws Exception {
		BatchResult result = new BatchResult();
		SessionManager sm = SiteManager.Start();
		try {
			sm.openTransaction();
			BatchManager bm = new BatchManager(sm, dep);
			bm.closeBatch(batch);
			result.setBatch(batch);
			sm.commitTransaction();
		}
		catch (Exception ex) {
			sm.rollbackTransaction();
			result.setException(ex);
			log.error(TITLE, ex);
		}
		finally {
			sm.closeSession();
		}
		return result;
	}
	
	protected BatchResult reloadBatch(Batch oldBatch,Department dep) throws Exception {
		BatchResult result = new BatchResult();
		SessionManager sm = SiteManager.Start();
		try {
			sm.openSession();
			Batch batch = sm.get(Batch.class, oldBatch.getId());
			batch.getPeriod().getDescription();
			batch.getPeriod().getBaseOrganization().getName();
			result.setBatch(batch);
		}
		catch (Exception ex) {
			result.setException(ex);
			log.error(TITLE, ex);
		}
		finally {
			sm.closeSession();
		}
		return result;
	}
	
	protected BatchResult getRemittancePostingSessionsByBatch(Batch batch) throws Exception {
		BatchResult result = new BatchResult();
		SessionManager sm = SiteManager.Start();
		try {
			sm.openSession();
			List<RemittancePostingSession> sessions = sm.execute("FROM RemittancePostingSession session " +
					"WHERE session.closed IS NULL " +
					"AND session.batch=:batch").setParameter("batch", batch).list();
			result.setRemittancePostingSessions(sessions);
		}
		catch (Exception ex) {
			result.setException(ex);
			log.error(TITLE, ex);
		}
		finally {
			sm.closeSession();
		}
		return result;
	}
	
	private BatchResult getBatchByID(BatchJob job, Department moduleDepartment) throws Exception {
		BatchResult result = new BatchResult();
		SessionManager sm = SiteManager.Start();
		try {
			Batch batch = (Batch)sm.execute("FROM Batch b " +
					"LEFT JOIN FETCH b.period p " +
					"LEFT JOIN FETCH p.baseOrganization " +
					"LEFT JOIN FETCH b.creator " +
					"LEFT JOIN FETCH b.closer " +
					"WHERE b.id=:batch_id")
					.setParameter("batch_id", job.getBatchID())
					.uniqueResult();
			if (batch != null) {
				batch.getEntries().size();
				for (Expected expected : batch.getExpecteds()) {
					expected.getAmount();
				}
			}
			result.setBatch(batch);
		}
		catch (Exception ex) {
			result.setException(ex);
			log.error(TITLE, ex);
		}
		finally {
			sm.closeSession();
		}
		return result;
	}
	
	private BatchResult setLastUsedBatch(BatchJob job, Department moduleDepartment) throws Exception {
		BatchResult result = new BatchResult();
		SessionManager sm = SiteManager.Start();
		try {
			sm.openTransaction();
			Passport currentPassport = getCurrentPassport(sm);
			LastUsedBatch lastUsedBatch = BatchUtils.updateLastUsedBatch(currentPassport, job.getBatch(), moduleDepartment); 
			sm.saveRAD(lastUsedBatch);
			sm.commitTransaction();
			log.info(String.format("Last used Batch for %S is set to %s", currentPassport.getName(), job.getBatch() != null ? BeanFormatter.getBatchDescription(lastUsedBatch.getBatch()) : "null"));
		}
		catch (Exception ex) {
			sm.rollbackTransaction();
			result.setException(ex);
			log.error(TITLE, ex);
		}
		finally {
			sm.closeSession();
		}
		return result;
	}
	
	private BatchResult getRecentBatch(BatchJob job, Department moduleDepartment) throws Exception {
		BatchResult result = new BatchResult();
		SessionManager sm = SiteManager.Start();
		try {
			Passport currentPassport = getCurrentPassport(sm);
			// AES-9323
			Batch recentBatch = BatchUtils.getLastUsedBatch(currentPassport, moduleDepartment);
			// 1. user.lastUsedBatch is applicable?
			if (recentBatch == null || !canUseBatch(recentBatch, currentPassport, moduleDepartment)) {
				// 2. the user has open owned batches? (the last created will be used)
				recentBatch = (Batch)sm.execute("FROM Batch b " +
						"WHERE b.creator=:creator " +
						"AND b.closed IS NULL " +
						"AND b.period.baseOrganization=:bo " +
						"ORDER BY created DESC")
						.setParameter("creator", currentPassport)
						.setParameter("bo", moduleDepartment)
						.setMaxResults(1).uniqueResult();
			}
			if (recentBatch == null || !canUseBatch(recentBatch, currentPassport, moduleDepartment)) {
				// 3. there are shared batches? (the last created will be used)
				recentBatch = (Batch)sm.execute("FROM Batch b " +
						"WHERE b.isShared = true " +
						"AND b.closed IS NULL " +
						"AND b.period.baseOrganization=:bo " +
						"ORDER BY created DESC")
						.setParameter("bo", moduleDepartment)
						.setMaxResults(1).uniqueResult();
			}
			if ((recentBatch == null || !canUseBatch(recentBatch, currentPassport, moduleDepartment)) && PassportManager.isPassportInRole(currentPassport, RoleKind.SYSADM, moduleDepartment)) {
				// SYSADM can use every batch
				recentBatch = (Batch)sm.execute("FROM Batch b " +
						"WHERE b.closed IS NULL " +
						"AND b.period.baseOrganization=:bo " +
						"ORDER BY created DESC")
						.setParameter("bo", moduleDepartment)
						.setMaxResults(1).uniqueResult();
			}
			if (recentBatch != null) {
				recentBatch.getPeriod().getDescription();
				recentBatch.getPeriod().getBaseOrganization().getName();
			}
			result.setBatch(recentBatch);
		}
		catch (Exception ex) {
			result.setException(ex);
			log.error(TITLE, ex);
		}
		finally {
			sm.closeSession();
		}
		return result;
	}
	
	private boolean canUseBatch(Batch batch, Passport currentPassport, Department moduleDepartment) throws Exception {
		if (batch == null)
			return false;
		
		// AES-13434
		if (batch.getClosed() != null)
			return false;
		
		if (!batch.getPeriod().getBaseOrganization().getId().equals(moduleDepartment.getId()))
			return false;
		
		return BatchUtils.canUseBatch(batch, currentPassport);
	}
	
	private Passport getCurrentPassport(SessionManager sm) throws Exception {
		return sm.get(Passport.class, ServiceSessionManager.getConnection().getRuntimePassport().getId());
	}
}
